La criminalidad en el mundo ha sido un tema a tratar desde el inicio de la sociedad. Los estados y países de todo el mundo se han propuesto bajar los índices de delitos, ya que hoy en día es imposible evitar cualquier clase de delito, pero esto no siempre se puede concretar.
Él data set que tenemos a disposición para el análisis representa las características de algunos de los llamados de emergencia registrados, los cuales son meritorios de llamarse delitos, por ende, fueron atendidos por la policía local.
En el siguiente data set tomaremos como labor principal la inspección de los datos recopilados en este data set, el análisis y la detección de problemáticas relacionadas a estos llamados.
Data set: https://www.kaggle.com/datasets/jgiigii/uscrimesdataset
En la siguiente sección veremos de manera básica de que se trata los datos recopilados dentro de este data set, esto con el fin de detectar anomalías y problemas, los cuales podemos solucionar eventualmente.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import plotly.express as px
from wordcloud import WordCloud
from sklearn.preprocessing import StandardScaler, MinMaxScaler, OneHotEncoder, LabelEncoder
from datetime import datetime
na_values = [' ']#?
# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory
data = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/Crimes_With_Dates_Cleaned.csv',
na_values = na_values)
En primera instancia, veremos la totalidad de filas y columnas, a manera de cuantificar la cantidad de datos que tenemos a disposición de análisis.
rows = len(data.axes[0])
cols = len(data.axes[1])
print("Número de filas: " + str(rows))
print("Número de columnass: " + str(cols))
data.head(3)
Se puede observar que existe una columna sin datos ni nombre, por ende se eliminará.
data.drop(labels = ["Unnamed: 0"], inplace = True, axis = 1)
Mostramos las primeras 3 filas de nuestra data set, ya con la columna sin nombre eliminada
data.head(3)
Una vez visualizado el conjunto de datos, de manera resumida, se listarán las columnas de nuestro dataset, incluyendo, también, el tipo de dato que representa
# Tipo de dato de cada columna
data.dtypes
Las variables presentes en la colección se clasifican como variables cuantitativas y variables cualitativas incluyendo el detalle de cada una de ellas como se muestra a continuación.
1.- Variables cuantitativas
2.- Variables Cualitativas
data.head(2)
Para saber si el dataset cumple es viable es necesario saber si posee missing values, óseas celdas que no poseen valor. También debemos saber que atributos no cumplen o no aportan valores a nuestro trabajo.
Bloc con sangría
Ahora se listará los atributos junto a la contabilización que presentan de valores nulos o blancos.
data.isnull().sum()
import seaborn as sns
# Identificamos los missing values visualmente
sns.heatmap(data.isnull(), cbar=False)
Se puede observar que variables como dispach_date_time, BlockAdress, Street Prefix y End_date_time, por mencionar algunas, presentan gran cantidad datos considerados como N/A o que no han sido registrados, por ejemplo, Street Suffix presenta 300662 datos nulos o en blanco de un total de 306094 registros aproximadamente.
Se eliminarán variables de poca relevancia para nuestro análisis, bajo los siguientes criterios:
Street Suffix,Street Prefix y Street Type: son datos irrelevantes además de contener bastantes datos nulos o en blanco, por ende se considera que el valor que pueden aportar al análisis es de poco interés.Street name: la información que aporta también a la la variable Block Adress y más completa para efectos de nuestro proyectoAgency: es información irrelevante, ya que la información aportada con esta variable es la misma que Police District Name, pero de forma más escueta
-Cr Number: Representa solo un ID del caso, misma información que obtenemos con Incident IDStart_Date_Timey End_Date_Time: representa fechas y horas de inicio del caso y fin, ambas variables son poco relevantes, ya que Dispach_Date_Time representa de mejor manera la información que necesitamos para abordar nuestra problemática.Latitude, Longitude, Place y PRA: la dimension espacial se utiliza mejor mediante la variable Location.Year-Month: para representar el mes y el año, se utiliza month y yearBasándose en la sección anterior, se procede a eliminar los atributos que no aportan o se encuentran con número grande de valores NA, y también se precede a eliminar los valores NA.
## Eliminación de atributos
data = data.drop(columns=["Incident ID","Offence Code","Agency","CR Number","Street Suffix","Block Address","Address Number","Street Prefix","Start_Date_Time","NIBRS Code","Location","Police District Name","State","Zip Code","End_Date_Time","PRA","Year-Month","Police District Number"])
data.dropna(subset=['Crime Name1',"Street Name", "Beat",'Crime Name2','Dispatch Date / Time', 'Crime Name3','City','Sector',"Street Type","Place"], inplace=True)
data.isnull().sum()
sns.heatmap(data.isnull(), cbar=False)
Realizando una sumatoria de todos los valores NA existentes en el dataset, se puede ver que no existen valores NA. De la misma forma mediante el diagrama es posible ver que no existe presencia de estos valores.
rows = len(data.axes[0])
cols = len(data.axes[1])
print("Número de filas: " + str(rows))
print("Número de columnass: " + str(cols))
Finalmente de poseer 306094 filas y 36 columnas, luego de la limpieza de datos y la agregación de columnas en la sección 2.1 obtenemos un dataset con 253852 filas y 17 columnas
Se realiza un "split" en la variable Dispach Date Time, la cual corresponde al momento exacto en la cual se acudió a la emergencia. De esto obtenemos:
date_complete: Momento en el que se enviaron a los agentes policiales en formato datetime.Year: Año cuando se realizó el delitoMonth: Mes cuando se realizó el delitoDay: Dia cuando se realizó el delitoHour: Hora cuando se realizó el delitoDayOfYear: Dia del año cuando se comete el delitoWeek: Número de la semana cuando se comete el delitoQuarter: Cuarto del día cuando se comete el delitoDayOfWeek: Número de la semana cuando se comete el delitomonth_name: Nombre del mes cuando se comete el delitoday_name: Nombre del día cuando se comete el delitoLo anterior se realiza con el objetivo de poseer variables que ayuden al análisis
data["date_complete"] = [datetime.strptime(data.iloc[i]["Dispatch Date / Time"], '%m/%d/%Y %I:%M:%S %p') for i in range(len(data.index))]
data["date_complete"] = pd.to_datetime(data["Dispatch Date / Time"])
data['Year'] = [(data.iloc[i]['date_complete']).year for i in range(len(data.index))]
data['Month'] = [(data.iloc[i]['date_complete']).month for i in range(len(data.index))]
data['Day'] = [(data.iloc[i]['date_complete']).day for i in range(len(data.index))]
data['Hour'] = [(data.iloc[i]['date_complete']).hour for i in range(len(data.index))]
data['DayOfYear'] = [(data.iloc[i]['date_complete']).dayofyear for i in range(len(data.index))]
data['Week'] = [(data.iloc[i]['date_complete']).week for i in range(len(data.index))]
data['DayOfWeek'] = [(data.iloc[i]['date_complete']).dayofweek for i in range(len(data.index))]
data['Quarter'] = [(data.iloc[i]['date_complete']).quarter for i in range(len(data.index))]
data["month_name"] = data["date_complete"].apply(lambda x: x.month_name())
data["day_name"] = data["date_complete"].apply(lambda x: x.day_name())
data.drop(labels = ["Dispatch Date / Time"], inplace = True, axis = 1)
data.groupby('City')['Victims'].count().sort_values(ascending=False)
en el siguiente grafico se puede expresar de mejor forma el comportamiento de nuestros datos
import seaborn as sns
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
data['City'].value_counts().plot(ax=ax, kind='bar', xlabel='Ciudades', ylabel='Numero de Crimenes')
plt.show()
Tenemos en el dataset registro de delitos de 54 ciudades, en donde:
En el se puede observar que claramente exten ciudades como Silver Spring tienen una gran repercusion dentro del conjunto de datos, en comparacion con ciudades como Sandy Spring
Por lo tanto se decide utilizar las ciudades con más de 300 víctimas por crímenes. El filtrado de ciudades se realiza en la seccion posterior debido a la conveninencia en la obtención de datos.
Dado los tipos de datos que se tienen dentro del dataset, se ha decidido como equipo que es necesario buscar otas fuentes de datos para complementar nuestro data set como lo es el caso del siguinete ejemplo del analisis de la ciudad de Montgomery County
Creemos que las variables meteorológicas pueden ser utiles para determinar los comportamientos de los delitos en nuestro dataset
En base a la variable City se obtienen una serie de variables meteorológicas mediante un script de python utilizando la librería y api de world weather online. Estas variables están asociadas a cada crimen por la ciudad y hora de crimen.
La obtención de datos meteorológicos se realiza utilizando la librería y api de world weather online. Se obtiene los datos mediante el código postal, estos códigos postales fueron determinados de forma manual debido a que la api rechazaba los nombre por City. Una vez obtenido los datos se guardan de forma manual en el repositorio debido a la ciento de miles de solicitudes realizadas en momento de ejecución, de forma que se ahorra el tiempo de compilación de este documento. Debido a lo anterior se comentará la siguiente sección de código y se mantendrá en esta sección en el documento con el fin de acreditar la obtención de datos.
# use package: https://github.com/ekapope/WorldWeatherOnline
#from wwo_hist import retrieve_hist_data
# daily frequency.
#frequency=1
#start_date = '2016-05-01'
#end_date = '2022-09-01'
#api_key = 'SECRET_KEY'
#######SILVERSPRING 20902 - ROCKVILLE 20848 - GAITHERSBURG 20877 - GERMANTOWN 20874 - BETHESDA 20811 - MONTGOMERY VILLAGE 20879 -
#######CHEVY CHASE 20814 - TAKOMA PARK 20910 -POTOMAC 20827 - KENSINGTON 20895 - BURTONSVILLE 20866 - OLNEY 20830 - DERWOOD 20855 -
#######CLARKSBURG 20876 - # DAMASCUS 20872 - BOYDS 20841 - POOLESVILLE 20837 - BROOKEVILLE 20833 - SANDY SPRING 20860 - ASHTON 20861 -
#######DICKERSON 20842 - CABIN JOHN 20817 - SPENCERVILLE 20868 - GLEN ECHO 20812
##location_list = ['20902', '20848']
##location_list = ['20877','20874']
##location_list = ['20811',
##location_list = ['20879']
##location_list = ['20814','20910']
##location_list = ['20827']
##location_list = ['20895']
##location_list = ['20866','20830']
##location_list = ['20855','20876']
##location_list = ['20872','20841']
##location_list = ['20837','20833']
#location_list = ['20860'
#location_list ='20861']
#location_list = ['20842','20817']
#location_list = ['20868','20812']
# this one runs for all days from start_date to end_date
# and saves the results as csv to the current directory.
#hist_weather_data = retrieve_hist_data(api_key,
# location_list,
# start_date,
# end_date,
# frequency,
# location_label = False,
# export_csv = True,
# store_df = True)
Luego de obtenido y subido al repositorio los datos meteorológicos, se obtienen estos datos de forma individual por cada Ciudad. Recordemos que esta ciudad debe tener más de 300 victimas
SILVERSPRING_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/SILVERSPRING_20902_climate.csv',na_values = na_values)
BETHESDA_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/BETHESDA_20811_climate.csv',na_values = na_values)
CHEVY_CHASE_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/CHEVY_CHASE_20814_climate.csv',na_values = na_values)
POTOMAC_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/POTOMAC_20827_climate.csv',na_values = na_values)
BROOKEVILLE_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/BROOKEVILLE_20833_climate.csv',na_values = na_values)
POOLESVILLE_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/POOLESVILLE_20837_climate.csv',na_values = na_values)
BOYDS_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/BOYDS_20841_climate.csv',na_values = na_values)
ROCKVILLE_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/ROCKVILLE_20848_climate.csv',na_values = na_values)
DERWOOD_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/DERWOOD_20855_climate.csv',na_values = na_values)
SANDY_SPRING_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/SANDY_SPRING_20860_climate.csv',na_values = na_values)
BURTONSVILLE_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/BURTONSVILLE_20866_climate.csv',na_values = na_values)
DAMASCUS_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/DAMASCUS_20872_climate.csv',na_values = na_values)
GERMANTOWN_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/GERMANTOWN_20874_climate.csv',na_values = na_values)
CLARKSBURG_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/CLARKSBURG_20876_climate.csv',na_values = na_values)
GAITHERSBURG_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/GAITHERSBURG_20877_climate.csv',na_values = na_values)
MONTGOMERY_VILLAGE_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/MONTGOMERY_VILLAGE_20879_climate.csv',na_values = na_values)
KENSINGTON_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/KENSINGTON_20895_climate.csv',na_values = na_values)
TAKOMA_PARK_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/TAKOMA_PARK_20910_climate.csv',na_values = na_values)
OLNEY_climate = pd.read_csv('https://gitlab.com/ricardomillanaob1/datasets/-/raw/master/climate/OLNEY_20830_climate.csv',na_values = na_values)
A modo de comparar los datos meteorológicos se necesita dar un formato especial a la variable date complete en el dataframe de data y los datos obtenidos del clima. Esto se realiza mediante la generación de una nueva variable: comparative_date, la cual junte la fecha (año-mes-día) y la hora
#Se realiza un formato comparable por cada hora
data["comparative_date"] = data["date_complete"].dt.strftime("%Y-%m-%d %H")
#Se realiza una trasnformacion del tipo de variable para que pueda ser comparable
data["comparative_date"] = data["comparative_date"].astype('str')
Ahora se procederá a juntar los datos del clima por cada ciudad mediante la hora en que se realiza un crimen.
#Se aisla la ciudad de forma individual. Se realiza por cada ciudad para evitar la comparación dual de fecha y ubicacion
SILVERSPRING = data[data.City=="SILVER SPRING"]
#Formato de fecha datetime
SILVERSPRING_climate["date_complete"] = [datetime.strptime(SILVERSPRING_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(SILVERSPRING_climate.index))]
#Se realiza un formato comparable por cada hora
SILVERSPRING_climate["comparative_date"] = SILVERSPRING_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
#Se realiza una trasnformacion del tipo de variable para que pueda ser comparable
SILVERSPRING_climate["comparative_date"] = SILVERSPRING_climate["comparative_date"].astype('str')
# Merge de datos
SILVERSPRING =pd.merge(SILVERSPRING,SILVERSPRING_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
BETHESDA = data[data.City=="BETHESDA"]
BETHESDA_climate["date_complete"] = [datetime.strptime(BETHESDA_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(BETHESDA_climate.index))]
BETHESDA_climate["comparative_date"] = BETHESDA_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
BETHESDA_climate["comparative_date"] = BETHESDA_climate["comparative_date"].astype('str')
BETHESDA =pd.merge(BETHESDA,BETHESDA_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
CHEVY_CHASE = data[data.City=="CHEVY CHASE"]
CHEVY_CHASE_climate["date_complete"] = [datetime.strptime(CHEVY_CHASE_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(CHEVY_CHASE_climate.index))]
CHEVY_CHASE_climate["comparative_date"] = CHEVY_CHASE_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
CHEVY_CHASE_climate["comparative_date"] = CHEVY_CHASE_climate["comparative_date"].astype('str')
CHEVY_CHASE=pd.merge(CHEVY_CHASE,CHEVY_CHASE_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
POTOMAC = data[data.City=="POTOMAC"]
POTOMAC_climate["date_complete"] = [datetime.strptime(POTOMAC_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(POTOMAC_climate.index))]
POTOMAC_climate["comparative_date"] = POTOMAC_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
POTOMAC_climate["comparative_date"] = POTOMAC_climate["comparative_date"].astype('str')
POTOMAC=pd.merge(POTOMAC,POTOMAC_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
BROOKEVILLE = data[data.City=="BROOKEVILLE"]
BROOKEVILLE_climate["date_complete"] = [datetime.strptime(BROOKEVILLE_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(BROOKEVILLE_climate.index))]
BROOKEVILLE_climate["comparative_date"] = BROOKEVILLE_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
BROOKEVILLE_climate["comparative_date"] = BROOKEVILLE_climate["comparative_date"].astype('str')
BROOKEVILLE=pd.merge(BROOKEVILLE,BROOKEVILLE_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
POOLESVILLE = data[data.City=="POOLESVILLE"]
POOLESVILLE_climate["date_complete"] = [datetime.strptime(POOLESVILLE_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(POOLESVILLE_climate.index))]
POOLESVILLE_climate["comparative_date"] = POOLESVILLE_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
POOLESVILLE_climate["comparative_date"] = POOLESVILLE_climate["comparative_date"].astype('str')
POOLESVILLE=pd.merge(POOLESVILLE,POOLESVILLE_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
BOYDS = data[data.City=="BOYDS"]
BOYDS_climate["date_complete"] = [datetime.strptime(BOYDS_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(BOYDS_climate.index))]
BOYDS_climate["comparative_date"] = BOYDS_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
BOYDS_climate["comparative_date"] = BOYDS_climate["comparative_date"].astype('str')
BOYDS=pd.merge(BOYDS,BOYDS_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
ROCKVILLE = data[data.City=="ROCKVILLE"]
ROCKVILLE_climate["date_complete"] = [datetime.strptime(ROCKVILLE_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(ROCKVILLE_climate.index))]
ROCKVILLE_climate["comparative_date"] = ROCKVILLE_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
ROCKVILLE_climate["comparative_date"] = ROCKVILLE_climate["comparative_date"].astype('str')
ROCKVILLE=pd.merge(ROCKVILLE,ROCKVILLE_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
DERWOOD = data[data.City=="DERWOOD"]
DERWOOD_climate["date_complete"] = [datetime.strptime(DERWOOD_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(DERWOOD_climate.index))]
DERWOOD_climate["comparative_date"] = DERWOOD_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
DERWOOD_climate["comparative_date"] = DERWOOD_climate["comparative_date"].astype('str')
DERWOOD=pd.merge(DERWOOD,DERWOOD_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
SANDY_SPRING = data[data.City=="SANDY SPRING"]
SANDY_SPRING_climate["date_complete"] = [datetime.strptime(SANDY_SPRING_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(SANDY_SPRING_climate.index))]
SANDY_SPRING_climate["comparative_date"] = SANDY_SPRING_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
SANDY_SPRING_climate["comparative_date"] = SANDY_SPRING_climate["comparative_date"].astype('str')
SANDY_SPRING=pd.merge(SANDY_SPRING,SANDY_SPRING_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
BURTONSVILLE = data[data.City=="BURTONSVILLE"]
BURTONSVILLE_climate["date_complete"] = [datetime.strptime(BURTONSVILLE_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(BURTONSVILLE_climate.index))]
BURTONSVILLE_climate["comparative_date"] = BURTONSVILLE_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
BURTONSVILLE_climate["comparative_date"] = BURTONSVILLE_climate["comparative_date"].astype('str')
BURTONSVILLE=pd.merge(BURTONSVILLE,BURTONSVILLE_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
DAMASCUS = data[data.City=="DAMASCUS"]
DAMASCUS_climate["date_complete"] = [datetime.strptime(DAMASCUS_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(DAMASCUS_climate.index))]
DAMASCUS_climate["comparative_date"] = DAMASCUS_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
DAMASCUS_climate["comparative_date"] = DAMASCUS_climate["comparative_date"].astype('str')
DAMASCUS=pd.merge(DAMASCUS,DAMASCUS_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
GERMANTOWN = data[data.City=="GERMANTOWN"]
GERMANTOWN_climate["date_complete"] = [datetime.strptime(GERMANTOWN_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(GERMANTOWN_climate.index))]
GERMANTOWN_climate["comparative_date"] = GERMANTOWN_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
GERMANTOWN_climate["comparative_date"] = GERMANTOWN_climate["comparative_date"].astype('str')
GERMANTOWN=pd.merge(GERMANTOWN,GERMANTOWN_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
CLARKSBURG = data[data.City=="CLARKSBURG"]
CLARKSBURG_climate["date_complete"] = [datetime.strptime(CLARKSBURG_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(CLARKSBURG_climate.index))]
CLARKSBURG_climate["comparative_date"] = CLARKSBURG_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
CLARKSBURG_climate["comparative_date"] = CLARKSBURG_climate["comparative_date"].astype('str')
CLARKSBURG=pd.merge(CLARKSBURG,CLARKSBURG_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
GAITHERSBURG = data[data.City=="GAITHERSBURG"]
GAITHERSBURG_climate["date_complete"] = [datetime.strptime(GAITHERSBURG_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(GAITHERSBURG_climate.index))]
GAITHERSBURG_climate["comparative_date"] = GAITHERSBURG_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
GAITHERSBURG_climate["comparative_date"] = GAITHERSBURG_climate["comparative_date"].astype('str')
GAITHERSBURG=pd.merge(GAITHERSBURG,GAITHERSBURG_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
MONTGOMERY_VILLAGE = data[data.City=="MONTGOMERY VILLAGE"]
MONTGOMERY_VILLAGE_climate["date_complete"] = [datetime.strptime(MONTGOMERY_VILLAGE_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(MONTGOMERY_VILLAGE_climate.index))]
MONTGOMERY_VILLAGE_climate["comparative_date"] = MONTGOMERY_VILLAGE_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
MONTGOMERY_VILLAGE_climate["comparative_date"] = MONTGOMERY_VILLAGE_climate["comparative_date"].astype('str')
MONTGOMERY_VILLAGE=pd.merge(MONTGOMERY_VILLAGE,MONTGOMERY_VILLAGE_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
KENSINGTON = data[data.City=="KENSINGTON"]
KENSINGTON_climate["date_complete"] = [datetime.strptime(KENSINGTON_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(KENSINGTON_climate.index))]
KENSINGTON_climate["comparative_date"] = KENSINGTON_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
KENSINGTON_climate["comparative_date"] = KENSINGTON_climate["comparative_date"].astype('str')
KENSINGTON=pd.merge(KENSINGTON,KENSINGTON_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
TAKOMA_PARK = data[data.City=="TAKOMA PARK"]
TAKOMA_PARK_climate["date_complete"] = [datetime.strptime(TAKOMA_PARK_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(TAKOMA_PARK_climate.index))]
TAKOMA_PARK_climate["comparative_date"] = TAKOMA_PARK_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
TAKOMA_PARK_climate["comparative_date"] = TAKOMA_PARK_climate["comparative_date"].astype('str')
TAKOMA_PARK=pd.merge(TAKOMA_PARK,TAKOMA_PARK_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
OLNEY = data[data.City=="OLNEY"]
OLNEY_climate["date_complete"] = [datetime.strptime(OLNEY_climate.iloc[i]["date_time"], '%Y-%m-%d %H:%M:%S') for i in range(len(OLNEY_climate.index))]
OLNEY_climate["comparative_date"] = OLNEY_climate["date_complete"].dt.strftime("%Y-%m-%d %H")
OLNEY_climate["comparative_date"] = OLNEY_climate["comparative_date"].astype('str')
OLNEY=pd.merge(OLNEY,OLNEY_climate[["comparative_date","tempC","windspeedKmph","humidity","precipMM","cloudcover"]],left_on="comparative_date",right_on="comparative_date")
# Se juntan todas las ciudad y forman un nuevo dataset
data = pd.concat([SILVERSPRING,BETHESDA,CHEVY_CHASE,POTOMAC,BROOKEVILLE,POOLESVILLE,BOYDS,ROCKVILLE,DERWOOD,SANDY_SPRING,BURTONSVILLE,DAMASCUS,GERMANTOWN,CLARKSBURG,GAITHERSBURG,MONTGOMERY_VILLAGE,KENSINGTON,TAKOMA_PARK,OLNEY])
Creemos que las fechas conmemorativas o festividades pueden tener alguna incidencia en la ocurrencia de crimenes, es por eso que se establece una nueva variable binaria que indique si el delito ocurrió un día festivo.
data['Is_Commemoration_Day'] =np.where((data['Day']==1) & (data['Month']==1),1,0)# New Years Day
data['Is_Commemoration_Day'] =np.where((data['Day']==16) & (data['Month']==1),1,0)# Marthin Luhter king day
data['Is_Commemoration_Day'] =np.where((data['Day']==17) & (data['Month']==3),1,0)# St. Patrick's Day
data['Is_Commemoration_Day'] =np.where((data['Day']==29) & (data['Month']==5),1,0)# Memorial Day
data['Is_Commemoration_Day'] =np.where((data['Day']==4) & (data['Month']==7),1,0)# Independence Day
data['Is_Commemoration_Day'] =np.where((data['Day']==4) & (data['Month']==9),1,0)# Labor Day
data['Is_Commemoration_Day'] =np.where((data['Day']==10) & (data['Month']==10),1,0)# Veterans Day
data['Is_Commemoration_Day'] =np.where((data['Day']==23) & (data['Month']==11),1,0)# Thanksgiving
data['Is_Commemoration_Day'] =np.where((data['Day']==25) & (data['Month']==12),1,0)# Chtismas
data['Committed_At_Morning'] = np.where(data['Committed_At_Morning'] == True, 1, 0) #Transfomacion a binario
data.isnull().sum()
data.info()
Dentro de las variables cuantitativas, se ha decidido exponer la relaciones entre esta mediante una matriz de correlacion, de esta forma es posible obtener de manera previa, una idea de como se comportan los datos entre si, esto con los datos adicionados anteriormente
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
corr_df = data.corr(method='pearson')
plt.figure(figsize=(20, 15))
sns.heatmap(corr_df, annot=True)
plt.show()
Luego de efectuar el análisis exploratorio de los datos, seguimos con la entrega de la información de manera visual, de esta forma lograremos captar de mejor manera la información entregada por el data set.
Para visualizar los cambios efectuados en el tratamiento de fechas y con el fin de ejemplificar la concurrencia de delitos se visualizara a continuacion los crimenes cometidos en cada uno de los meses del año
#Delitos por año y mes
import plotly.express as px
month_order = ["January", "February", "March", "April", "May",
"June", "July", "August", "September",
"October", "November", "December"]
year_order = np.sort(data.Year.unique())
fig = px.histogram(
data, x = "month_name", barmode = "group",
color = "Year",
category_orders = {"Year": year_order, "month": month_order}
)
fig.show()
A partir del año 2017 existe una disminución de los delitos cometidos, sin embargo, el año 2017 y 2022 poseen una diferencia considerable con los demás años.
Los delitos cometidos durante el año 2016 son registrados desde el mes de julio hasta el mes de diciembre, mientras que el año 2022 los delitos son registrados desde el mes de enero hasta una parte del mes de agosto. Debido a lo anterior estos años poseen una diferencia en sus registros respecto a los otros años.
Tambien se visualizara los delitos cometidos en cada uno de los dias de la semana
import plotly.express as px
px.defaults.template = "seaborn"
days_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
year_order = np.sort(data.Year.unique())
fig = px.histogram(
data, x = "day_name",
category_orders = {"day_name": days_order, "Year": year_order},
color = "Committed_At_Morning", barmode = "group",
)
fig.show()
En este gráfico se puede observar que el día martes es el día en el cual se reciben más alertas de delitos durante una semana, también es apreciable que los días domingos son los días con menos alertas de delitos registrados en esta muestra, también podemos observar que los delitos son alertados mayormente en el horario de la tarde, con más del doble de alertas, en comparación del horario de la mañana
Ahora se observará de manera más detallada el anterior gráfico, al visualizar la proporción de las alertas por horas y no por jornadas.
days_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
d = data
d["Hour"] = d["Hour"].astype(int).astype(str)
fig = px.histogram(
d, x = "Hour", barmode = "group",
category_orders = {
"Year": year_order,
"Hour": sorted(range(24))
},
)
fig.add_annotation(
x = 4, y = 4500,
arrowcolor = "black", arrowwidth = 2,
text = "Tendencia baja de crimenes",
)
fig.show()
La actividad delictual expresada en las horas del día, se puede observar una mayor actividad en el rango de las 15 a 16 horas, y la menor actividad se detecta en el rango horario de las 4 a 5 de la madrugada.
fig = px.pie(
data.dropna(subset = ["Crime Name1"]), names = "Crime Name1",
labels={"Crime Name1": "Crime type:"},
title = "Type of crime",
)
fig.update_layout(
title = dict(
font_size = 30,
),
legend = dict(
bgcolor="LightSteelBlue",
font_size = 15
),
)
fig.show()
El gráfico visualiza de manera eficaz el porcentaje que representa cada uno de los tipos de delitos dentro de la muestra. El mayor porcentaje con un 50.2% de los delitos registrados es en contra de la propiedad, le sigue con un 22.2% de delitos varios, con un 15.7% se acontecen alertas por crímenes contra la sociedad, el 10.7% de las alertas corresponden a crímenes contra la persona y por último, el 1.31% son alertas que no corresponden a ningún crimen en concreto.
Teniendo la información de las cantidades y comportamientos del registro de alertas delictivas durante horas y días, nos compete definir a que corresponden estas alertas
En este gráfico observamos la cantidad de llamados de emergencia a lo largo de los años, los cuales fueron categorizados dependiendo del tipo de crimen cometido.
data_groupby_year = data.groupby(['Year', 'Crime Name1']).size().unstack().reset_index(inplace=False)
crimes = ['Crime Against Person', 'Crime Against Property', 'Crime Against Society', 'Not a Crime', 'Other']
data_groupby_year.plot(x='Year', y=crimes, kind="line", figsize=(8, 8))
plt.legend(loc='upper right')
plt.title('Cantidad de Crimenes comentidos por Tipo de Crimen')
plt.show()
plt.clf()
Se observa que la tendencia de las alertas corresponden a los crímenes que atentan contra la propiedad, también se aprecia un declive en las alertas en el último año, esto se puede explicar, ya que el año 2022 aún no termina, por ende los registros se encuentran incompletos
Se pretende ahora definir con mayor detalle la proporción que tiene cada tipo de alerta delictiva dentro del registro de datos
data_group_by_year_crime_2 = data.groupby(['Year', 'Crime Name2'])['Crime Name2'].count()
# plot the result
data_group_by_year_crime_2.unstack().plot()
plt.xticks(rotation=45)
plt.legend(bbox_to_anchor = (0.65, 1.25))
plt.show()
Destacamos que existen un tipo de crimen que destaca por sobre el resto el cual tuvo su frecuencia de más alta en el año 2018 y luego empieza una caída sustancial en esa cantidad de crimenes.
En el siguiente grafico se muestra la relacion que tiene la cantidad de delitos efectuados con respecto al secto asignado, esto depende netamente de la ubicacion del delito
fig = px.pie(
data.dropna(subset = ["Sector"]), names = "Sector",
labels={"Sector": "Sector:"},
title = "Sectores",
)
fig.update_layout(
title = dict(
font_size = 30,
),
legend = dict(
bgcolor="LightSteelBlue",
font_size = 15
),
)
fig.show()
Este grafico nos muestra que las zonas de conflicto, en proporcion son el sector P, A y G sinedo estos los que presentan una diferencia mayor con respecto al resto de sectores
fig = px.pie(
data.dropna(subset = ["Street Type"]), names = "Street Type",
labels={"Street Type": "Street Type:"},
title = "Street Type",
)
fig.update_layout(
title = dict(
font_size = 30,
),
legend = dict(
bgcolor="LightSteelBlue",
font_size = 15
),
)
fig.show()
Con el fin de conciliar una idea con respecto a la razon principal de la delictualidad, ahora se vera un grafico que representa la tasa de delictualidad conforme a la temperatura del dia en el cual se efectuo
d = data
d["tempC"] = d["tempC"].astype(int).astype(str)
fig = px.histogram(
d, x = "tempC", barmode = "group",
category_orders = {
"tempC": sorted(range(40))
},
)
fig.show()
En el grafico se muestra que en temperaturas extremas como 39,40 y 41 grados celcius, en caso de temperaturas altas y -16, -15 y -14 en las temperaturas bajas, se producen menos delitos que en las temperaturas mas templadas como 24 y 25 grados
Dada la información obtenida mediante los análisis previos hemos encontrado la siguiente problemática, hemos visto que existen diferencias de la cantidad de delitos cometidos entre día y noche, entre los meses e incluso entre los años, pero nos gustaría saber cómo podemos predecir estos crímenes. Para esto debemos tener en cuenta los siguientes pasos
Crime Name2 como un delito de robo?. ¿Es posible predecir los delitos de robo en base a esto? Con esto buscamos detectar patrones dentro los delitos y así, de esta forma, tomar medidas con respecto a estos patrones
En este experimento se busa responder la pregunta 1: " Debido a que el delito de robo es el que se produce con una mayor frecuencia, definimos el siguiente problema: ¿Qué variables son las que tienen mayor incidencia en clasificar Crime Name 2 como un delito de robo?. ¿Es posible predecir los delitos de robo en base a esto? ". Durante este experimento se utilizará el clasificador de arbol de desición, debido a que en el Hito 2 otorgó mejores resulados que el clasificador KNN
Crime Name 3¶types = data['Crime Name2'].unique()
types
Como se ve anteriormente existen varios delitos en la variable Crime Name3, los cuales, aunque no sean del tipo robo se encuentran almacenado dentro de "All Other Offence" en Crime Name2. Por esta razon se decide identificar y seleccionar los delitos de tipo robo desde la variable Crime Name3. Una vez identificado. Una vez identificados los delitos de tipo robo se almacenan en un dataframe data_robo para este experimento.
robo = ["LARCENY - FROM AUTO","ASSAULT - 2ND DEGREE","LARCENY - SHOPLIFTING ","LARCENY (DESCRIBE OFFENSE)","LARCENY - FROM BLDG","LARCENY - AUTO PARTS",
"AUTO THEFT - VEHICLE THEF","ASSAULT - SIMPLE","BURGLARY - NO FORCED ENTRY-RESIDENTIAL","BURGLARY - FORCED ENTRY-RESIDENTIAL","WEAPON - POSSESSION",
"ASSAULT - AGGRAVATED - NON-FAMILY-OTHER WEAPON","ASSAULT - AGGRAVATED - FAMILY-OTHER WEAPON","ASSAULT - AGGRAVATED - OTHER","LARCENY - POCKET PICKING",
"ROBBERY - STREET-STRONG-ARM","LARCENY - POSTAL","ASSAULT - AGGRAVATED - FAMILY-STRONG-ARM","LARCENY - FROM YARDS","ASSAULT - AGGRAVATED - GUN",
"BURGLARY - NO FORCED ENTRY-NONRESIDENTIAL","BURGLARY (DESCRIBE OFFENSE","ROBBERY - STRONG ARM","LARCENY - PURSE SNATCHING - NO FORCE","LARCENY - FROM MALLS",
"ASSAULT - INTIMIDATION (INCLUDES STALKING)","ROBBERY - GUN","ROBBERY - KNIFE","ROBBERY - STREET-GUN","STOLEN VEHICLE (DESCRIBE OFFENSE)","WEAPON - FIRING",
"ASSAULT - AGGRAVATED - NON-FAMILY-STRONG-ARM","ASSAULT - AGGRAVATED - NON-FAMILY-GUN","ROBBERY - BUSINESS-GUN","FIRE (NOT ARSON)","ROBBERY - DOMESTIC",
"ROBBERY - CARJACKING - ARMED","ARSON - BURNING OF - (IDENTIFY OBJECT)","ROBBERY - BUSINESS-STRONG-ARM","ROBBERY - RESIDENTIAL-GUN","ROBBERY - RESIDENTIAL-STRONG-ARM",
"ROBBERY - STREET-OTHER WEAPON","ROBBERY - BUSINESS-OTHER WEAPON","ROBBERY (DESCRIBE OFFENSE)","ROBBERY - OTHER WEAPON","LARCENY - FROM BANKING-TYPE INST",
"ROBBERY - BANKING-TYPE INST ","ROBBERY - CARJACKING - STRONG-ARM","STOLEN PROPERTY - POSSESS","ASSAULT - AGGRAVATED - POL OFF-OTHER WEAPON","LARCENY - FROM SHIPMENT",
"LARCENY - FROM COIN MACHINE","ASSAULT - AGGRAVATED - PUB OFF-OTHER WEAPON","ROBBERY - RESIDENTIAL-OTHER WEAPON","ROBBERY - FORCIBLE PURSE SNATCHING",
"ASSAULT - AGGRAVATED - POL OFF-STRONG-ARM","AUTO THEFT - THEFT AND USE VEHICLE OTHER CRIME","AUTO THEFT - THEFT AND SALE VEHICLE","LARCENY - THEFT OF US GOVERNMENT PROPERTY",
"AUTO THEFT - STRIP STOLEN VEHICLE","ASSAULT - AGGRAVATED - POL OFF-GUN","STOLEN PROPERTY - RECEIVE","STOLEN PROPERTY (DESCRIBE OFFENSE)"]
#Nueva columna donde se almacenan provisoriamente los valores de "Crime Name3" en formato str
data["crimenes"] = data["Crime Name3"].astype('str')
#Comprobar que los datos de la nueva columna existen en la lista robo
data['Is_Steal'] = [data.iloc[i]['crimenes'] in robo for i in range(len(data.index))]
#Asigna 1 si el delito corresponde a Robo, de lo contario 0
data['Is_Steal'] =np.where((data['Is_Steal']==True),1,0)
#Eliminación de columna provisoria
data.drop(labels = ["crimenes"], inplace = True, axis = 1)
# Datos de tipo robo
data_robo = data[data.Is_Steal==1]
Ahora se transformarán las variables String que se utilizarán para predecir el Target Crime Name 2
le = LabelEncoder()
data_robo['Sector'] = le.fit_transform(data_robo['Sector'] )
data_robo['Place'] = le.fit_transform(data_robo['Place'])
data_robo['Street Type'] = le.fit_transform(data_robo['Street Type'])
data_robo['Street Name'] = le.fit_transform(data_robo['Street Name'])
data_robo['Beat'] = le.fit_transform(data_robo['Beat'])
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
from sklearn.model_selection import train_test_split
from sklearn import tree
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
import numpy as np
Target='Crime Name2'
Predictors=['Year', 'Month', 'Day', 'Hour', 'DayOfYear','Week','DayOfWeek','Quarter',
'Sector','Place','Beat','Street Name','Street Type',"Is_Commemoration_Day"
,"humidity","precipMM","cloudcover","tempC","windspeedKmph","Latitude","Longitude"]
X=data_robo[Predictors].values
y=data_robo[Target].values
Se define el set de prueba y el de entrenamiento con un 33% de los datos como datos para realizar el testeo de la predicción y que siempre ocupe el mismo random para la evaluación del resultado el cual es la semilla 37.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.33, random_state=37)
Ahora se definen los criterios de entropy para el árbol de decisiones y como profundidad máxima del árbol 8 para evitar el sobreajuste del mismo.
#Se define los hiperparametros
clf = DecisionTreeClassifier(max_depth=8,criterion='entropy')
#Se crea el modelo de entrenamiento
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
print(classification_report(y_test, y_pred))
#print(confusion_matrix(y_test, y_pred))
print("Accuracy en test set:", accuracy_score(y_test, y_pred)) ## Evaluamos la predicción comparando y_test con y_pred
Se puede ver que existe una precisión sobre el 50% en algunos delitos, sin embargo, más de la mitad de los delitos se encuentras debajo del 50% de precisión
from sklearn.model_selection import KFold, cross_val_score
k_folds = KFold(n_splits = 10)
scores = cross_val_score(clf, X, y, cv = k_folds)
print(scores)
Como resultado obtenemos una lista que refleja los valores por cada validación de los datos de prueba, donde los valores rondan entre %41 y como máximo %55
Ahora en este segundo caso con la combinacion de splits de 15
from sklearn.model_selection import KFold, cross_val_score
k_folds = KFold(n_splits = 15)
score = cross_val_score(clf, X, y, cv = k_folds)
print("Cross Validation Scores are {}".format(score))
print("Average Cross Validation score :{}".format(score.mean()))
Como resultado obtenemos una lista que refleja los valores por cada validación de los datos de prueba, donde los valores rondan entre %40 y como máximo %58.3
import matplotlib.pyplot as plt
%matplotlib inline
feature_importances = pd.Series(clf.feature_importances_, index=Predictors)
feature_importances.nlargest(10).plot(kind='barh')
Las siete variables con mayor incidencias son (ordenadas de mayor a menor incidencia):
Place es una variable que nos indica el lugar donde se cometió el delito. Siendo el delito de tipo robo y siendo las variables Place, Hour, Longitude y Latitude con mayor incidencia, logramos establecer que el delito de tipo depende de la hora y el lugar para que pueda ser cometido.
Mediante el clasificador de Árbol de decisiones fué posible obtener una precisión sobre el 50% en ciertos delitos como: Weapon Law Violations, Theft From Motor Vehicle, Burglary/Breaking and Entering y All other Larceny. Sin embargo, otros delitos de tipo robo proseen una precisión menor al 50%. Estamos aplicando este modelo sin la utilización de técnicas que ayuden el desbalanceo de clases, por tanto, en esta instancia creemos que no es posible predecir los delitos de tipo robo.
El siguiente experimento es un problema de clasificacion para categorizar los crimenes de acuedo a la columna "Crime Name 1" Para este experimento se utilizara;
Se define la variable target y las columnas usadas para la predicción.
El siguiente caso nuestro target serán los Crime Name1 ya que buscamos predecir esta columna en este método experimental.
Mientras que las variables utilizadas para predecir la columna Crime Name1 son las mencionadas en el Modelo Propuesto.
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
from sklearn.model_selection import train_test_split
from sklearn import tree
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
import numpy as np
TargetVariable='Crime Name1'
Predictors=['Victims', 'Year', 'Month', 'Hour', 'Day', 'Committed_At_Morning','Is_Commemoration_Day',"humidity","precipMM","cloudcover"]
X=data[Predictors].values
y=data[TargetVariable].values
Se define el set de prueba y el de entrenamiento con un 33% de los datos como datos para realizar el testeo de la predicción y que siempre ocupe el mismo random para la evaluación del resultado el cual es la semilla 37.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.33, random_state=37)
Ahora se definen los criterios de entropy para el árbol de decisiones y como profundidad máxima del árbol 3 para evitar el sobreajuste del mismo.
#Se define los hiperparametros
clf = DecisionTreeClassifier(max_depth=3,criterion='entropy')
#Se crea el modelo de entrenamiento
clf.fit(X_train,y_train)
y_pred = clf.predict(X_test)
print(classification_report(y_test, y_pred))
print(confusion_matrix(y_test, y_pred))
print("Accuracy en test set:", accuracy_score(y_test, y_pred)) ## Evaluamos la predicción comparando y_test con y_pred
Se observa una precisión bastante alta en Crime Against Person, pero no se puede tener certeza de que estos valores nos permitan realmente predecir correctamente todo los crimenes ya que existen 2 categorías en un 0% de predicción por los datos.
En el caso de la matriz de confusión observamos que el que tiene más aciertos es Crime
Para medir el desempeño del modelo utilizaremos cross validation, en este primer caso con la combinación de splits de 10
from sklearn.model_selection import KFold, cross_val_score
k_folds = KFold(n_splits = 10)
scores = cross_val_score(clf, X, y, cv = k_folds)
print(scores)
Como resultado obtenemos una lista que refleja los valores por cada validación de los datos de prueba, donde los valores rondan entre 38% y como máximo 63%
Ahora en este segundo caso con la combinacion de splits de 15
k_folds = KFold(n_splits = 15)
score = cross_val_score(clf, X, y, cv = k_folds)
print("Cross Validation Scores are {}".format(score))
print("Average Cross Validation score :{}".format(score.mean()))
Como resultado obtenemos una lista que refleja los valores por cada validación de los datos de prueba, donde los valores rondan entre 36% y como máximo 63%
Creamos nuestro X e y de entrada y los sets de entrenamiento y test a partir de los atributos que utilicemos.
# knn
from sklearn.preprocessing import MinMaxScaler
from sklearn.neighbors import KNeighborsClassifier
X = data[['Victims', 'Year', 'Month', 'Hour', 'Day', 'Committed_At_Morning','Is_Commemoration_Day',"humidity","precipMM","cloudcover"]].values
y = data['Crime Name1'].values
Entrenamiento del dataset a partir del target y la data
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=20)
scaler = MinMaxScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
Creacion del clasificador knn, a partir de los features de prueba
knn = KNeighborsClassifier(10)
knn.fit(X_train, y_train)
print('Accuracy of K-NN classifier on training set: {:.2f}'
.format(knn.score(X_train, y_train)))
print('Accuracy of K-NN classifier on test set: {:.2f}'
.format(knn.score(X_test, y_test)))
pred = knn.predict(X_test)
print(confusion_matrix(y_test, pred))
print(classification_report(y_test, pred))
Para medir el desempeño del modelos utilizaremos cross validation, ya que nos permitira testear el modelo a partir de la cantidad de segmetaciones que utilicemos
from sklearn.model_selection import KFold, cross_val_score
k_folds = KFold(n_splits = 10)
scores = cross_val_score(knn, X, y, cv = k_folds)
print(scores)
Como resultado obtenemos una lista que refleja los valores por cada validación de los datos de prueba, donde los valores como minimo son de 35% y como máximo un 53%
Si bien se logró compara los resultados de la clasificación entre Árbol de decisiones y KNN se observa un mejor resultado en el método de Árbol de decisiones, pero no son concluyentes a responder las problemáticas ya que sus valores no son muy representativos en cuanto a todos los resultados posibles en el problema. Ademas podemos concluir que nuestro dataset no posee datos equilibrados en cuanto al atributo que queremos predecir y tambien en posible que se necesite aplicar otras tecnicas mas avanzadas para obtener resultados mas alentadores.
El siguiente experimento es un problema de segmentacion o agrupacion de los llamados de emergencia del dataset, para categorizar los crimenes de acuerdo si son; leves, medios, graves; Para este experimento se utilizara;
El algoritmo que utilizaremos es:
from sklearn.cluster import KMeans
Con el dataset ya limpio podemos utilizarlo para entrenar al algortimo y encontrar el resultado mas optimo. Para este modelo utilizaremos las columnas:
X = data[['Victims', 'Year', 'Month', 'Hour', 'Day', 'Committed_At_Morning','Is_Commemoration_Day',"humidity","precipMM","cloudcover"]]
Ejecutemos K-Means entre 1 y 24 clusters
random_state = 20
sse = []
clusters = list(range(1, 24))
for k in clusters:
kmeans = KMeans(n_clusters=k)
kmeans = kmeans.fit(X)
sse.append(kmeans.inertia_)
Se realiza la implementación del método del codo, para poder apreciar la cantidad mas correcta con la que trabajar.
plt.plot(clusters, sse, marker="o")
plt.title("Metodo del codo de 1 a 24 clusters")
plt.grid(True)
plt.show()
El gráfico nos muestra el error de K-Means usando diferentes números de clusters. Acá se puede notar que un valor óptimo es 3
Con el valor ya encontrado podemos entrenar de mejor manera el modelo con K-Means
kmeans = KMeans(n_clusters=3, n_init=20, max_iter=300, random_state=random_state).fit(X)
y_pred = kmeans.predict(X)
y_pred
Con la funcion bicount podemos encontrar la cantidad de datos que posee cada cluster
from collections import Counter
Counter(kmeans.labels_)
Ahora graficamos para apreciar el enfoque visual de los posibles clusters generados.
from sklearn.decomposition import PCA
new_X = PCA(n_components=2, random_state=0).fit_transform(X)
plt.scatter(new_X[:, 0], new_X[:, 1], c=kmeans.labels_)
plt.title("K-Means")
plt.show()
from sklearn.metrics import silhouette_score
print("Dataset X K-Means\t", silhouette_score(X, kmeans.labels_))
Fue posible obtener un optimo resultado al agrupar los direntes delitos?
Dado el experimento realizado con la tecnica de clustering, en primera instancia la hipotesis inicial daba 3 variables iniciales que podian representar grupos con caracteristicas similares para establecer la gravedad de un llamado de emergencia, bajo esta premisa el metodo del codo nos arrojo la el resultado esperado
#%%shell
#jupyter nbconvert --to html /content/file.ipynb